package com.example.demo1.controller;

import com.example.demo1.common.ResultAjax;
import com.example.demo1.common.SessionUtils;
import com.example.demo1.model.ArticleInfo;
import com.example.demo1.model.UserInfo;
import com.example.demo1.model.VO.UserInfoVO;
import com.example.demo1.service.ArticleService;
import com.example.demo1.service.UserService;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.catalina.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 言初
 * Date: 2024-06-19
 * Time: 18:16
 */
@RestController
@RequestMapping("/art")
public class ArticleController {

    // 注入我们自己写的线程池
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;


    //注入service 依赖
    @Autowired
    private ArticleService articleService;
    @Autowired
    private UserService userService;


    // 对于从文章正文中拿到文章摘要的操作，因为一个用户有多篇文章，所以这里使用多线程的方法
    // 来进行处理会比较的合适和高效
    // 这里定义一个全局变量来表示文章简介摘要的长度
    private static final int _DESC_LENGTH = 120;// 文章简介的长度




    // 得到当前登录用户的文章列表
    @RequestMapping("/mylist")
    public ResultAjax myList(HttpServletRequest request){
        // 1. 得到当前登录的用户
        UserInfo userInfo = SessionUtils.getUser(request);
        if(userInfo == null){
            //未登录状态
            return ResultAjax.fail(-1,"请先登录！");
        }
        // 2.根据用户id查询此用户发表的所有文章
        // controller方法需要去调用service方法，service方法需要去调用mapper方法
        List<ArticleInfo> list = articleService.getListByUid(userInfo.getId());
        // 处理list -> 将文章正文变成简介(多线程)
        // 对于从文章正文中拿到文章摘要的操作，因为一个用户有多篇文章，所以这里使用多线程的方法
        // 来进行处理会比较的合适和高效
        if(list !=null && list.size()>0){
            //并行处理list合集
            list.stream().parallel().forEach((art) -> {
                if(art.getContent().length() > _DESC_LENGTH){
                    art.setContent(art.getContent().substring(0,_DESC_LENGTH));
                }
            });
        }
        // 3. 把结果返回给前端
        return ResultAjax.succ(list);
    }



    // 删除文章操作
    @RequestMapping("/del")
    public ResultAjax del(Integer aid,HttpServletRequest request){
        // 参数使用的是Integer而不是int,是因为传过来的参数有可能为null，所以为了防止出错这里使用Integer来进行接收
        // 1.参数校验
        // System.out.println(aid);
        if(aid==null || aid<=0){
            return ResultAjax.fail(-1,"参数有误");
        }
        // 2. 得到当前登录的用户
        UserInfo userInfo = SessionUtils.getUser(request);
        if(userInfo==null){
            return ResultAjax.fail(-2,"请先登录！");
        }
        // 3. 判断文章归属人 ，删除文章操作 where id = aid and uid = uid
        int result = articleService.del(aid, userInfo.getId());
        // 4. 将结果返回给前端
        return ResultAjax.succ(result);
    }



    // 新增文章的操作
    @RequestMapping("/add")
    public ResultAjax add(ArticleInfo articleInfo,HttpServletRequest request){
        // 1. 参数校验
        if(articleInfo==null || !StringUtils.hasLength(articleInfo.getTitle()) ||
        !StringUtils.hasLength(articleInfo.getContent())){
            // 参数异常
            return ResultAjax.fail(-1,"非法参数");
        }
        // 2. 组装数据
        UserInfo userInfo = SessionUtils.getUser(request);
        if(userInfo==null){
            return ResultAjax.fail(-2,"请先登录");
        }
        articleInfo.setUid(userInfo.getId());
        // 3. 将数据存到数据库中
        int result = articleService.add(articleInfo);
        // 4. 将结果返回给前端
        return ResultAjax.succ(result);
    }



    // 从数据库中取出某篇文章的操作
    @RequestMapping("/update_init")
    public ResultAjax updateInit(Integer aid,HttpServletRequest request){
        // 1. 校验参数
        if(aid==null || aid<=0){
            // 参数异常
            return ResultAjax.fail(-1,"参数有误！");
        }
        // 2. 得到当前登录的用户ID
        UserInfo userInfo = SessionUtils.getUser(request);
        if(userInfo==null){
            return ResultAjax.fail(-2,"请先登录！");
        }
        // 3. 查询文章对象，并验证文章归属人
        ArticleInfo articleInfo = articleService.getArticleByIdAndUid(aid, userInfo.getId());
        // 4. 将结果返回给后端
        return ResultAjax.succ(articleInfo);
    }



    // 修改文章
    @RequestMapping("update")
    public ResultAjax update (ArticleInfo articleInfo,HttpServletRequest request){
        // 1. 校验参数 对象不为null，文章标题不为空也不为null，文章正文不为空也不为null
        if(articleInfo==null || !StringUtils.hasLength(articleInfo.getTitle()) ||
        !StringUtils.hasLength(articleInfo.getContent())){
            // 参数有误
            return ResultAjax.fail(-1,"非法参数");
        }
        // 2. 获取登录用户
        UserInfo userInfo = SessionUtils.getUser(request);
        if(userInfo==null){
            // 用户未登录
            return ResultAjax.fail(-2,"请先登录！");
        }

        //把查询到的用户ID，放到文章对象中
        articleInfo.setUid(userInfo.getId());
        // 3. 修改文章，并验证文章归属人
        int result = articleService.update(articleInfo);
        // 4. 返回结果
        return ResultAjax.succ(result);
    }



    // 查询文章详情，并把文章相关数据和作者相关数据返回给前端进行显示
    // 采用多线程并发 的方式来执行
    @RequestMapping("/detail")
    public ResultAjax detail(Integer aid) throws ExecutionException, InterruptedException {
        // 1. 校验参数
        if(aid==null || aid<=0){
            return ResultAjax.fail(-1,"非法参数！");
        }
        // 2. 根据文章ID查询到文章的详情信息
        ArticleInfo articleInfo = articleService.getDetailById(aid);
        if(articleInfo==null || articleInfo.getId()<=0){
            return ResultAjax.fail(-1,"非法参数！");
        }

        // 3. 根据查到的文章对象中的用户ID，来查询用户的详情
        // 4. 根据用户ID在文章表中查询用户发表的总文章数
        // 3、4 步会使用线程的方式来进行处理
        FutureTask<UserInfoVO> userTask = new FutureTask<>(()->{
            return userService.getUserById(articleInfo.getUid());
        });
        taskExecutor.submit(userTask);

        FutureTask<Integer> artCountTask = new FutureTask<>(()->{
            return articleService.getArtCountByUid(articleInfo.getUid());
        });
        taskExecutor.submit(artCountTask);

        // 5.组装数据（这一步是要等到第3、4步完成之后才能执行的,在futuretask中有一个get方法可以实现这个功能）
        UserInfoVO userInfoVO = userTask.get();// 等待 userTask 任务执行完成
        int artCount = artCountTask.get(); // 等待 artCountTask 任务执行完成
        userInfoVO.setArtCount(artCount);

        // 将找到的文章对象和用户对象存储到map（键值对的方式）中
        HashMap<String ,Object> result = new HashMap<>();
        result.put("user",userInfoVO);
        result.put("art",articleInfo);
        // 将结果返回给前端
        return ResultAjax.succ(result);
    }



    // 阅读量加一的操作
    @RequestMapping("/increment_rcount")
    public ResultAjax incrementRCount(Integer aid){
        // 1.参数校验
        if(aid==null || aid<=0){
            return ResultAjax.fail(-1,"参数有误！");
        }
        // 2. 更新数据库
        int result = articleService.incrementRCount(aid);
        // 3.返回结果
        return ResultAjax.succ(result);
    }



    // 分页功能的页面初始化，查询本页码下的文章然后显示在页面上
    @RequestMapping("/getlistbypage")
    public ResultAjax getListByPage(Integer pindex ,Integer psize) throws ExecutionException, InterruptedException {
        // 1.加工参数,参数矫正
        if(pindex == null || pindex<1){
            pindex = 1; // 参数矫正
        }
        if(psize == null || psize <1){
            psize = 2; // 参数矫正
        }

        // 2. 并发进行文章列表和总页数的查询
        // 2.1 查询分页文章列表数据
        int finalOffset = psize*(pindex-1); // 偏移量
        int finalPsize = psize ; // 每页有多少文章
        FutureTask<List<ArticleInfo>> listTask = new FutureTask<>(()->{
            return articleService.getListByPage(finalPsize,finalOffset);
        });

        // 2.2 查询总页数
        FutureTask<Integer> sizeTask = new FutureTask<>(()->{
            // 文章的总数
            int totalCount = articleService.getCount();

            // 对于总页数需要使用到向上取整的方式，这个方式首先需要把文章总数与每页显示条数相除用double接收
            // 然后调用 math中的ceil方法就可以实现向上取整的功能，然后这个方法的返回值也是double
            // 所以最后还需要进行一波强制类型转换，来转换成 int 类型
            double sizeTemp = (totalCount*1.0) / (finalPsize*1.0);
            return (int)Math.ceil(sizeTemp);
        });


        // 将这2个任务加入到线程池中
        taskExecutor.submit(listTask);
        taskExecutor.submit(sizeTask);
        // 3. 数据组装
        List<ArticleInfo> list = listTask.get();
        int size  = sizeTask.get();
        // 使用map（键值对的方式）进行数据的组装
        HashMap<String ,Object> map = new HashMap<>();
        map.put("list",list);
        map.put("size",size);
        // 4. 将结果返回给前端
        return ResultAjax.succ(map);
    }


}
